// Copyright 2023 Specter Ops, Inc.
// 
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// SPDX-License-Identifier: Apache-2.0

//go:build ignored
// +build ignored

package main

// This program generates headers.go. It can be invoked by running go generate

import (
	"bytes"
	"encoding/csv"
	"go/format"
	"log"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"
)

var packageTemplate = template.Must(template.New("").Parse(`// Code generated by generate.go; DO NOT EDIT.
// This file was generated at {{ .Timestamp }}
// using data from {{ .URL }}
package headers

const (
{{- range .Headers }}
	{{ printf "%s Header = \"%s\" // %s" .Symbol .FieldName .Reference }},
{{- end }}
)
`))

type TemplateData struct {
	Timestamp time.Time
	URL       string
	Headers   []Header
}

type Header struct {
	Symbol    string
	FieldName string
	Reference string
}

func main() {
	const (
		url             = "https://www.iana.org/assignments/http-fields/field-names.csv"
		headersFilename = "headers.go"
	)

	if _, err := os.Lstat(headersFilename); err == nil {
		// Only generate the file if it has been removed as generation requires a call to IANA
		return
	}

	if res, err := http.Get(url); err != nil {
		log.Fatal(err)
	} else if out, err := os.Create("headers.go"); err != nil {
		log.Fatal(err)
	} else {
		defer res.Body.Close()
		defer out.Close()

		if headers, err := csv.NewReader(res.Body).ReadAll(); err != nil {
			log.Fatal(err)
		} else {
			entries := []Header{}
			for _, row := range headers[1:] {
				if row[0] != "*" && row[2] == "permanent" {
					entries = append(entries, Header{
						Symbol:    strings.ReplaceAll(row[0], "-", ""),
						FieldName: row[0],
						Reference: row[3],
					})
				}
			}

			var buf bytes.Buffer
			if err := packageTemplate.Execute(&buf, TemplateData{
				Timestamp: time.Now(),
				URL:       url,
				Headers:   entries,
			}); err != nil {
				log.Fatal(err)
			} else if data, err := format.Source(buf.Bytes()); err != nil {
				log.Fatal(err)
			} else if _, err := out.Write(data); err != nil {
				log.Fatal(err)
			}
		}
	}
}
